﻿using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using Urs.Core;
using Urs.Data.Domain.Users;
using Urs.Data.Domain.Configuration;
using ExternalAuth.WeixinOpen.Core;
using ExternalAuth.WeixinOpen.Models;
using Urs.Services.Authentication;
using Urs.Services.Authentication.External;
using Urs.Services.Configuration;
using Urs.Services.Users;
using Urs.Services.Localization;
using Urs.Services.Logging;
using Urs.Services.Security;
using Urs.Framework;
using Urs.Framework.Controllers;
using Weixin.UrSDK.App;
using ExternalAuth.WeixinOpen;

namespace ExternalAuth.WeixinOpen.Controllers
{
    public class ExternalAuthWeixinOpenController : BasePluginController
    {
        #region Fields

        private readonly ILocalizationService _localizationService;
        private readonly IPermissionService _permissionService;
        private readonly ISettingService _settingService;
        private readonly UserSettings _userSettings;
        private readonly IOpenAuthenticationService _openAuthenticationService;
        private readonly ExternalAuthSettings _externalAuthenticationSettings;
        private readonly WeixinOpenSettings _weixinOpenSettings;
        private readonly IWebHelper _webHelper;
        private readonly ILogger _logger;
        private readonly IUserService _userService;
        private readonly IUserRegistrationService _userRegistrationService;
        private readonly IAuthenticationService _authenticationService;
        #endregion

        #region Ctor

        public ExternalAuthWeixinOpenController(
            ILocalizationService localizationService,
            IPermissionService permissionService,
            ISettingService settingService, UserSettings userSettings,
            WeixinOpenSettings weixinOpenSettings,
            IOpenAuthenticationService openAuthenticationService,
            ExternalAuthSettings externalAuthenticationSettings,
            IWebHelper webHelper,
            ILogger logger,
            IUserService userService,
            IUserRegistrationService userRegistrationService,
            IAuthenticationService authenticationService)
        {
            this._localizationService = localizationService;
            this._permissionService = permissionService;
            this._settingService = settingService;
            this._userSettings = userSettings;
            this._weixinOpenSettings = weixinOpenSettings;
            this._openAuthenticationService = openAuthenticationService;
            this._externalAuthenticationSettings = externalAuthenticationSettings;
            this._webHelper = webHelper;
            this._logger = logger;
            this._userService = userService;
            this._userRegistrationService = userRegistrationService;
            this._authenticationService = authenticationService;
        }

        #endregion

        #region Util

        #endregion
        [AdminAuthorize]
        [Area("Admin")]
        public IActionResult Configure()
        {
            var model = new ConfigurationModel();
            model.AppId = _weixinOpenSettings.AppId;
            model.AppSecret = _weixinOpenSettings.AppSecret;

            return View("~/Plugins/ExternalAuth.WeixinOpen/Views/Configure.cshtml", model);
        }

        [HttpPost]
        [AdminAuthorize]
        [Area("Admin")]
        public IActionResult Configure(ConfigurationModel model)
        {
            if (!ModelState.IsValid)
                return Json(new { error = 1 });

            //save settings
            _weixinOpenSettings.AppId = model.AppId;
            _weixinOpenSettings.AppSecret = model.AppSecret;
            _settingService.SaveSetting(_weixinOpenSettings);

            return Json(new { success = 1 });
        }

        public IActionResult PublicInfo()
        {
            var model = new ConfigurationModel();
            model.AppId = _weixinOpenSettings.AppId;
            model.AppSecret = _weixinOpenSettings.AppSecret;
            return View("~/Plugins/ExternalAuth.WeixinOpen/Views/PublicInfo.cshtml", model);
        }

        static Dictionary<string, ApiResponse<TokenEntity>> collection = new Dictionary<string, ApiResponse<TokenEntity>>();
        private readonly object @lock = new object();
        [HttpPost]
        public ApiResponse<TokenEntity> LoginApi(MoLogin login)
        {
            ApiResponse<TokenEntity> result = null;
            var secondRequest = false;
            lock (@lock)
            {
                secondRequest = collection.ContainsKey(login.code);
            }

            if (!secondRequest)
            {
                //第一次请求
                lock (@lock)
                {
                    collection[login.code] = null;
                }
            }
            else
            {
                //第二次请求
                lock (@lock)
                {
                    result = collection[login.code];
                }
            }

            try
            {
                var oiask = new AppOAuth2API().GetOpenIdAndSessionKey(login.code, _weixinOpenSettings.AppId, _weixinOpenSettings.AppSecret);

                if (oiask == null)
                    return ApiResponse<TokenEntity>.Warn("回调内容异常");

                if (!string.IsNullOrEmpty(oiask.errmsg))
                    return ApiResponse<TokenEntity>.Warn(string.Format("回调异常,errcode:{0} errmsg:{1}", oiask.errcode, oiask.errmsg));

                var parameters = new OAuthAuthParameters(Provider.SystemName);
                parameters.OpenId = oiask.openid;
                parameters.OAuthToken = oiask.session_key;
                var userFound = _openAuthenticationService.GetUser(parameters);

                if (userFound == null)
                {
                    try
                    {
                        var user = new AppOAuth2API().GetUser(login.encryptedData, login.iv, oiask.session_key);
                        _logger.Information(Newtonsoft.Json.JsonConvert.SerializeObject(user));
                        parameters.UserClaim.Nickname = user.nickName;
                        parameters.UserClaim.HeadImgUrl = user.avatarUrl;
                        parameters.UserClaim.Sex = user.gender;
                        parameters.UserClaim.Province = user.province;
                        parameters.UserClaim.City = user.city;
                        parameters.UnionId = user.unionId;
                    }
                    catch (Exception ex)
                    {
                        _logger.Error(ex.Message);
                    }

                    userFound = _userService.InsertGuestUser();
                    _openAuthenticationService.AssociateExternalAccountWithUser(userFound, parameters);

                    _userRegistrationService.RegisterUser(request: new UserRegistrationRequest()
                    {
                        User = userFound,
                        IsApproved = true,
                        PasswordFormat = PasswordFormat.Clear,
                        UserRegistrationType = UserRegistrationType.AutoToken,
                    });
                }

                if (!string.IsNullOrEmpty(login.state))
                {
                    var inviteUser = _userService.GetUserByCode(login.state);
                    if (inviteUser != null && inviteUser.Active && !inviteUser.Deleted)
                    {
                        userFound.AffiliateId = inviteUser.Id;
                        _userService.UpdateUser(userFound);
                    }
                }

                var token = _authenticationService.JwtSignIn(userFound, _userSettings.JwtSecurityKey, _userSettings.JwtExpires, _userSettings.JwtIssuer, _userSettings.JwtAudience);

                var model = new TokenEntity() { token = token, openId = oiask.openid, guid = userFound.UserGuid.ToString() };

                result = ApiResponse<TokenEntity>.Success(model);
            }
            catch (Exception ex)
            {
                return ApiResponse<TokenEntity>.Warn(ex.Message);
            }

            if (result != null)
            {
                lock (@lock)
                {
                    collection[login.code] = result;
                }
            }
            return result;
        }

        [HttpPost]
        public ApiResponse<PhoneEntity> GetPhoneNumber(MoPhone phone)
        {
            var oiask = new AppOAuth2API().GetOpenIdAndSessionKey(phone.code, _weixinOpenSettings.AppId, _weixinOpenSettings.AppSecret);

            if (oiask == null)
                return ApiResponse<PhoneEntity>.Warn("回调内容异常");

            if (!string.IsNullOrEmpty(oiask.errmsg))
                return ApiResponse<PhoneEntity>.Warn(string.Format("回调异常,errcode:{0} errmsg:{1}", oiask.errcode, oiask.errmsg));


            var parameters = new OAuthAuthParameters(Provider.SystemName);
            parameters.OpenId = oiask.openid;
            parameters.OAuthToken = oiask.session_key;
            var userFound = _openAuthenticationService.GetUser(parameters);

            if (userFound != null)
            {
                var api = new AppOAuth2API().GetPhoneNumber(phone.encryptedData, phone.iv, oiask.session_key);

                if (api == null)
                    return ApiResponse<PhoneEntity>.Warn("获取不到手机号");

                userFound.PhoneNumber = api.phoneNumber;
                _userService.UpdateUser(userFound);

                return ApiResponse<PhoneEntity>.Success(new PhoneEntity() { phone = api.phoneNumber, guid = userFound.UserGuid.ToString(), openId = parameters.OpenId });
            }
            return ApiResponse<PhoneEntity>.Warn("用户不存在");
        }

    }
}